package com.quanyou.utils;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * 可设置过期时间的{@link HashMap}
 * 
 * @author yuanxl
 * 
 * @param <T>
 * @param <K>
 */
public class ExpiredHashMap<T, K> implements Map<T, K> {

    private volatile DelayQueue<Element<T>> queue = new DelayQueue<Element<T>>();
    private ConcurrentMap<T, K> container = new ConcurrentHashMap<T, K>();
    private volatile boolean running = false;

    // init time for millis
    private static final long ORIGIN_MILL = System.currentTimeMillis();
    private final Listener listener = new Listener();

    public ExpiredHashMap() {
        this.running = true;
        this.listener.setName("ExpiredHashMap-DelayQueue-" + this);
        this.listener.setDaemon(true);
        this.listener.start();
    }

    private class Listener extends Thread {

        public Listener() {
        }

        @Override
        public void run() {
            while (running && !isInterrupted()) {
                try {
                    Element<T> delay = queue.poll(5, TimeUnit.SECONDS);
                    if (delay != null) {
                        container.remove(delay.getT());
                    }
                } catch (InterruptedException e) {
                    // ignore interrupt
                }
            }
        }
    }

    /**
     * 添加
     * 
     * @param key
     * @param value
     * @param timeout 单位毫秒
     */
    public void put(T key, K value, long timeout) {
        Element<T> e = new Element<T>(key, timeout);
        if (containsKey(key)) {
            container.remove(key);
            queue.remove(e);
        }
        container.put(key, value);
        queue.add(e);
    }

    /**
     * 释放资源
     */
    public void destroy() {
        this.running = false;
        this.container.clear();
        this.queue.clear();
    }

    /**
     * 尽最大可能的释放资源
     */
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        destroy();
    }

    @SuppressWarnings("hiding")
    private class Element<T> implements Delayed {
        private T t;
        private long timeout;// 记录具体需要被delayed处理的偏移时间点,单位毫秒
        private long ct;// // 记录具体对象产生时的偏移时间点，单位ms

        public Element(T t, long timeout) {
            this.t = t;
            this.timeout = timeout;
            this.ct = System.currentTimeMillis() - ORIGIN_MILL;
        }

        public T getT() {
            return t;
        }

        @Override
        public int compareTo(Delayed o) {
            if (o == this) // compare zero ONLY if same object
                return 0;
            long d = (getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
            return (d == 0) ? 0 : ((d < 0) ? -1 : 1);
        }

        @Override
        public long getDelay(TimeUnit unit) {
            long now = System.currentTimeMillis() - ORIGIN_MILL;
            long d = unit.convert(ct + timeout - now, TimeUnit.MILLISECONDS);
            return d;
        }

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + getOuterType().hashCode();
            result = prime * result + ((t == null) ? 0 : t.hashCode());
            return result;
        }

        @SuppressWarnings("rawtypes")
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Element other = (Element) obj;
            if (!getOuterType().equals(other.getOuterType()))
                return false;
            if (t == null) {
                if (other.t != null)
                    return false;
            } else if (!t.equals(other.t))
                return false;
            return true;
        }

        @SuppressWarnings("rawtypes")
        private ExpiredHashMap getOuterType() {
            return ExpiredHashMap.this;
        }

    }

    @Override
    public int size() {
        return container.size();
    }

    @Override
    public boolean isEmpty() {
        return container.isEmpty();
    }

    @Override
    public void clear() {
        container.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return container.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return container.containsValue(value);
    }

    @Override
    public K get(Object key) {
        return container.get(key);
    }

    @Override
    public K put(T key, K value) {
        throw new UnsupportedOperationException("use method put(T t, K k, long timeout)");
    }

    @Override
    public K remove(Object key) {
        return container.remove(key);
    }

    @Override
    public void putAll(Map<? extends T, ? extends K> m) {
        throw new UnsupportedOperationException("use method put(T t, K k, long timeout)");
    }

    @Override
    public Set<T> keySet() {
        return container.keySet();
    }

    @Override
    public Collection<K> values() {
        return container.values();
    }

    @Override
    public Set<Entry<T, K>> entrySet() {
        return container.entrySet();
    }

}
